#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "ai.h"
#include "board.h"
#include "constants.h"
#include "utilities.h"

#include <stdio.h>

int evaluate(grid * g)
{
    return MONOTONICITY_WEIGHT * monotonicity1(g) +
    SMOOTHNESS_WEIGHT * smoothness1(g) +
    EMPTY_COUNT_WEIGHT * empty_count(g) +
    MAXVALUE_WEIGHT * max_value(g);
}

int solve(node * n, int(*compare)(int, int)) {
    if (n -> num_children == 0)
    {
        // YOU LOST
        return -1;
    }

    int i, opt_move = 0;
    for (i = 1; i < n -> num_children; ++i)
    {
        if (compare(n -> children[opt_move] -> score, n -> children[i] -> score)
             == n -> children[i] -> score)
        {
            opt_move = i;
        }
    }
    return opt_move;
}

int min_minimax(int depth, node * n)
{
    int i, j = 0, k;
    int create_children = FALSE;
    if (depth == 0)
    {
        return n -> score = evaluate(n -> g);
    }

    n -> score = INT_MAX;

//printf("min_minimax: n -> num_children: %d\n", n -> num_children);
    if (n -> num_children == 0)
    {
        create_children = TRUE;

        // add all its children
        n -> num_children = empty_count(n -> g) * NUM_COMPUTER_CHOICES;
        n -> children = (node**)malloc(n -> num_children * sizeof(node*));
    }

    for (i = 0; i < n -> num_children;)
    {
        for (k = 0; k < NUM_COMPUTER_CHOICES; ++k)
        {
            if (create_children)
            {
                for (; j < n -> g -> length; ++j)
                {
                    if (n -> g -> array[j] == 0)
                        break;
                }

                n -> children[i] = pop_node();
                n -> children[i] -> parent = n;
                n -> children[i] -> g = pop_grid();
                copy_into(n -> g, n -> children[i] -> g);

                n -> children[i] -> g -> array[j] = COMPUTER_CHOICES[k];
                n -> children[i] -> last_move = j;
            }
            n -> score = min(n -> score, max_minimax(depth - 1, n -> children[i]));
            ++i;
        }
        ++j;
    }

    if (n -> g != NULL)
    {
        push_grid(n -> g);
        n -> g = NULL;
    }

    return n -> score;
}

int max_minimax(int depth, node * n)
{
    int i, j;
    if (depth == 0)
    {
        return n -> score = evaluate(n -> g);
    }
    int create_children = FALSE;
//printf("max_minimax: n -> num_children: %d\n", n -> num_children);
	n -> score = LOSE;
    if (n -> children == NULL)
    {
        create_children = TRUE;

        // add all its children
        n -> children = (node**)malloc(4 * sizeof(node*));
		i = 0;
    }
	else
	{
		i = 4 - n -> num_children;
	}

    j = 0;
    for (; i < 4; ++i)
    {
        if (create_children)
        {
            n -> children[j] = pop_node();

            n -> children[j] -> parent = n;
            n -> children[j] -> g = pop_grid(); // g should always be NULL
            copy_into(n -> g, n -> children[j] -> g);

            direction_func[i](n -> children[j] -> g);

            // compare the grids
            if (memcmp(n -> g -> grid[0], n -> children[j] -> g -> grid[0], 
                    sizeof(int) * n -> g -> length) == 0)
            {
                push_node(n -> children[j]);
                continue;
            }

            n -> children[j] -> last_move = i;
        }

        n -> score = max(n -> score, min_minimax(depth - 1, n -> children[j]));
        j++;
    }

    n -> num_children = j;

    if (n -> g != NULL)
    {
        push_grid(n -> g);
        n -> g = NULL;
    }

    return n -> score - depth;
}
